<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="Go 错误处理（Error Wrapping）">
<meta name="twitter:description" content="<p>错误处理：类型断言；错误嵌套（Error Wrapping）:工具函数Unwrap、Is和As，自定义类型实现Unwrap；源码参考：error接口的实现，os包</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gotour-error-handling.html"/>
<meta property="og:title" content="来玩魔王的咚 | Go 错误处理（Error Wrapping）" />
<meta property="og:description" content="<p>错误处理：类型断言；错误嵌套（Error Wrapping）:工具函数Unwrap、Is和As，自定义类型实现Unwrap；源码参考：error接口的实现，os包</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>错误处理：类型断言；错误嵌套（Error Wrapping）:工具函数Unwrap、Is和As，自定义类型实现Unwrap；源码参考：error接口的实现，os包</p>" />

    <meta name="tags" content="gotour" />
    <meta name="tags" content="errors" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-11-21 10:00:00+08:00">
                <script>document.write(moment('2020-11-21 10:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-11-21 10:00:00+08:00">
                <script>document.write(moment('2020-11-21 10:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            Go 错误处理（Error Wrapping）
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#cuo-wu-chu-li">错误处理</a><ul>
<li><a href="#lei-xing-duan-yan">类型断言</a></li>
</ul>
</li>
<li><a href="#cuo-wu-qian-tao-error-wrapping">错误嵌套（Error Wrapping）</a><ul>
<li><a href="#gong-ju-han-shu">工具函数</a></li>
<li><a href="#zi-ding-yi-lei-xing-shi-xian-unwrap">自定义类型实现Unwrap</a></li>
</ul>
</li>
<li><a href="#yuan-ma-can-kao">源码参考</a><ul>
<li><a href="#error-jie-kou-de-shi-xian">error接口的实现</a></li>
<li><a href="#os-bao">os包</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="cuo-wu-chu-li"><a class="toclink" href="#cuo-wu-chu-li">错误处理</a></h3>
<p>在Go语言中，那种可以预期的错误，并且不是非常严重，不影响程序运行的，可以返回错误给调用者，让调用者自己决定如何处理。  </p>
<p>具体看这篇： <a href="https://steeed.gitee.io/gopl-error-handling.html">https://steeed.gitee.io/gopl-error-handling.html</a> 。  </p>
<h4 id="lei-xing-duan-yan"><a class="toclink" href="#lei-xing-duan-yan">类型断言</a></h4>
<p>自定义错误，携带更多错误信息：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">CodeError</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Code</span><span class="w"> </span><span class="kt">int</span><span class="w">    </span><span class="c1">// 错误码</span><span class="w"></span>
<span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="kt">string</span><span class="w">  </span><span class="c1">// 错误信息，把这个信息隐藏起来，所以是小写</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 实现error的接口</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w"> </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%v: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Code</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>如果需要取到结构体中详细的字段值，需要通过类型断言，把error接口类型转为实际的动态类型：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 直接返回错误</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">wrang</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">CodeError</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="mi">100</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;出错了&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">wrang</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">e</span><span class="p">.(</span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这个例子下面还要继续用。  </p>
<p>这里的例子用的是类型断言x.(T)。对于有分支的情况，使用类型分支：x.(type)也是差不多的。而且这已经是旧的处理方式了，不再继续展开。  </p>
<h3 id="cuo-wu-qian-tao-error-wrapping"><a class="toclink" href="#cuo-wu-qian-tao-error-wrapping">错误嵌套（Error Wrapping）</a></h3>
<p>error接口虽然简洁，但是功能也弱。之前的做法是一层一层在原有的错误信息上叠加新的错误信息，然后返回给调用者。所谓的叠加也只是字符串的拼接而已，没有层级。  </p>
<p>Error Wrapping是go1.13版本新加的。<br>
接口定义在fmt包中，定义如下：</p>
<div class="highlight"><pre><span></span><code><span class="c1">//fmt/errors.go</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">wrapError</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>err字段是原有的错误类型。msg字段用于存放新的错误信息。这样就实现了error的嵌套。  </p>
<p>使用示例：</p>
<div class="highlight"><pre><span></span><code><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;原始错误信息&quot;</span><span class="p">)</span><span class="w"></span>
<span class="nx">w</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息: %w&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w">  </span><span class="c1">// 现在有新的%w可以用</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">w</span><span class="p">)</span><span class="w"></span>
<span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息: %v&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w">  </span><span class="c1">// 过去用%v来叠加信息</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="c1">// 两个Pringln的输出都是一样的，具体如下：</span><span class="w"></span>
<span class="c1">// 新错误信息: 原始错误信息</span><span class="w"></span>
</code></pre></div>

<p>Go语言没有提供Wrap函数，而是新加了一个%w的格式。这里输出的消息和以前使用%v是一样的。  </p>
<h4 id="gong-ju-han-shu"><a class="toclink" href="#gong-ju-han-shu">工具函数</a></h4>
<p>Golang 1.13引入了wrapping error后，同时为errors包添加了3个工具函数，他们分别是Unwrap、Is和As。  </p>
<h5 id="errorsunwrap"><a class="toclink" href="#errorsunwrap">errors.Unwrap</a></h5>
<p>嵌套的目的是为了分清楚层级，通过errors.Unwrap函数可以获取到上一层的错误：</p>
<div class="highlight"><pre><span></span><code><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Unwrap</span><span class="p">(</span><span class="nx">w</span><span class="p">))</span><span class="w">                   </span><span class="c1">// 原始错误信息</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Unwrap</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Unwrap</span><span class="p">(</span><span class="nx">w</span><span class="p">)))</span><span class="w">   </span><span class="c1">// &lt;nil&gt;</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Unwrap</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span><span class="w">                   </span><span class="c1">// &lt;nil&gt;</span><span class="w"></span>
</code></pre></div>

<h5 id="errorsis"><a class="toclink" href="#errorsis">errors.Is</a></h5>
<p>用来判断1个error是不是包含有目标error。因为嵌套的关系，需要逐层解开确认。只要某一层包含目标error就返回true，如果是同一个error也返回true：</p>
<div class="highlight"><pre><span></span><code><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s">&quot;原始错误信息&quot;</span><span class="p">)</span><span class="w"></span>
<span class="nx">w1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息1: %w&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w"></span>
<span class="nx">w2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息2: %w&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Is</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span><span class="w"> </span><span class="nx">w1</span><span class="p">))</span><span class="w">  </span><span class="c1">// false</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Is</span><span class="p">(</span><span class="nx">w1</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">))</span><span class="w">  </span><span class="c1">// true</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Is</span><span class="p">(</span><span class="nx">w2</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">))</span><span class="w">  </span><span class="c1">// true</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Is</span><span class="p">(</span><span class="nx">w1</span><span class="p">,</span><span class="w"> </span><span class="nx">w2</span><span class="p">))</span><span class="w"> </span><span class="c1">// false</span><span class="w"></span>
<span class="nx">w0</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">w1</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nx">Is</span><span class="p">(</span><span class="nx">w0</span><span class="p">,</span><span class="w"> </span><span class="nx">w1</span><span class="p">))</span><span class="w"> </span><span class="c1">// true</span><span class="w"></span>
</code></pre></div>

<h5 id="erroras"><a class="toclink" href="#erroras">error.As</a></h5>
<p>上面的errors.Is是判断是否包含对应的类型，这里的errors.As则是转化成指定的类型。<br>
接着之前的例子，加一层嵌套后，类型断言就失败了：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">wrang</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">e</span><span class="p">.(</span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span><span class="w"></span>

<span class="w">    </span><span class="nx">w</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息: %w&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w"> </span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">w</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">c2</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">w</span><span class="p">.(</span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w">  </span><span class="c1">// 嵌套后，类型断言失败</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">c2</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">ok</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>接下来就要用到error.As函数，帮我们遍历所有的嵌套链，找到符合的类型完成类型转换：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">wrang</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"></span>

<span class="w">    </span><span class="nx">w</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;新错误信息: %w&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">)</span><span class="w"> </span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">ce</span><span class="w"> </span><span class="o">*</span><span class="nx">CodeError</span><span class="w">  </span><span class="c1">// 创建一个新的变量，用于接收值</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nx">As</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">ce</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  </span><span class="c1">// 把指针传给函数，函数内会对变量进行修改</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">ce</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="zi-ding-yi-lei-xing-shi-xian-unwrap"><a class="toclink" href="#zi-ding-yi-lei-xing-shi-xian-unwrap">自定义类型实现Unwrap</a></h4>
<p>函数errors.Is和errors.As都是通过调用error接口的Unwrap方法实现的。对于自定义错误类型，只需要再实现一个Unwrap方法来解开嵌套链，就可以了。<br>
为之前自定义的错误类型再实现一个Unwrap方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">CodeError</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Code</span><span class="w"> </span><span class="kt">int</span><span class="w">    </span><span class="c1">// 错误码</span><span class="w"></span>
<span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="kt">string</span><span class="w">  </span><span class="c1">// 错误信息，把这个信息隐藏起来，所以是小写</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 实现error的接口</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w"> </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%v: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Code</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">CodeError</span><span class="p">)</span><span class="w"> </span><span class="nx">Unwrap</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<h3 id="yuan-ma-can-kao"><a class="toclink" href="#yuan-ma-can-kao">源码参考</a></h3>
<p>尝试去源码里找一些答案。  </p>
<h4 id="error-jie-kou-de-shi-xian"><a class="toclink" href="#error-jie-kou-de-shi-xian">error接口的实现</a></h4>
<p>现在可以这么理解，error接口需要实现2个方法Error和Unwrap。不过Unwrap方法不是必须的。  </p>
<p>现在的error接口依然只有1个方法：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// builtin/builtin.go</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>所以，没有强制要求实现Unwrap方法。不过对于error接口，最好是把Unwrap也实现了。  </p>
<p>源码中是用下面的方法来检查接口的：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// erors/wrap.go</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">Unwrap</span><span class="p">(</span><span class="nx">err</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">err</span><span class="p">.(</span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">Unwrap</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="w">    </span><span class="p">})</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">ok</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">Unwrap</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>在调用error的Unwrap之前，前进行类型断言。直接使用接口的字面量检查是否实现了接口。没实现就返回nil，实现了就返回方法的调用结果。  </p>
<h4 id="os-bao"><a class="toclink" href="#os-bao">os包</a></h4>
<p>源码os包的错误处理值得参考，具体是<code>os/error.go</code>里的源码。  </p>
<p>接口的实现：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// PathError records an error and the operation and file path that caused it.</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">PathError</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Op</span><span class="w">   </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Path</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Err</span><span class="w">  </span><span class="kt">error</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">PathError</span><span class="p">)</span><span class="w"> </span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Op</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Path</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;: &quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="p">.</span><span class="nx">Error</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="w"> </span><span class="o">*</span><span class="nx">PathError</span><span class="p">)</span><span class="w"> </span><span class="nx">Unwrap</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里依然用旧的类型分支来判断错误类型：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// underlyingError returns the underlying error for known os error types.</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">underlyingError</span><span class="p">(</span><span class="nx">err</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">err</span><span class="p">.(</span><span class="kd">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="o">*</span><span class="nx">PathError</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nx">Err</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="o">*</span><span class="nx">LinkError</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nx">Err</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="o">*</span><span class="nx">SyscallError</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nx">Err</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>一方面Error Wrapping嵌套后只能用Is函数实现类型断言，没法用类型分支了。<br>
另一方面，可能是因为这里处理的是自己包里生成的错误，不会有外部再被嵌套过的情况。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>